import time
import copy
import numpy as np
from typing import Iterable, Iterator

sequence = ['3', '4', '5', '6', '7', '8', '9',
            '10', 'J', 'Q', 'K', 'A', '2', '小王', '大王']
card = ['单牌', '对牌', '三张',  '三带一', '单顺', '双顺', '三顺', '飞机','炸弹','四带二']

def to_card(data):
    return list(map(lambda x: sequence[x], filter(lambda x:x<len(sequence),data)))

def to_num(data):
    return list(map(lambda x: sequence.index(x), filter(lambda x:x in sequence,data)))

def to_samecard_count(data):
    item = list(set(data))
    item.sort()
    ret = (item,list(map(lambda x: data.count(x), item)))
    return ret

def contain_sequence(data,id=1):
    item = list(filter(lambda x:x<len(sequence)-3,data))#不包含双王和2
    item.sort()
    ret, temp = [],[]
    for i in range(len(item)):
        if item.count(item[i])>=id:
            if len(temp)==0 or item[i]-temp[-1]==1:
                temp.append(item[i])
            elif item[i]-temp[-1]!=0:
                if (id==1 and len(set(temp))>=5) or (id==2 and len(set(temp))>=3) or (id==3 and len(set(temp))>=2):
                    ret.append(list(temp))
                temp = [item[i]]
                
    if (id==1 and len(set(temp))>=5) or (id==2 and len(set(temp))>=3) or (id==3 and len(set(temp))>=2):
        ret.append(list(temp))
    return ret

def has_sequence(data):
    item = list(filter(lambda x:x<len(sequence)-3,data))#不包含双王和2
    item.sort()
    ret, count = [],[]
    for i in range(3):
        temp = contain_sequence(item,i+1)
        if len(temp)>0:
            ret.append(temp)
            count.append(i+1)
    return (ret,count)

def combine(data):
    ret = []
    for it in data:
        if len(it)>0 and isinstance(it[0],list):
            ret += combine(it)
        else:
            ret += it
    return ret
def remove(data,item):
    for it in item:
        data.remove(it)
    return data

def has_card(data):
    seq = has_sequence(data)
    it = to_samecard_count(data)

    if it[1].count(4)>=1 and (it[1].count(1)>=2 or it[1].count(2)>=1):
        return 9  # 四带二
    elif it[1].count(4)>=1 or (it.count(len(sequence)-1)==1 and it.count(len(sequence)-2)==1):
        return 8  # 炸弹
    elif seq[1].count(3)>0:
        th = seq[0][seq[1].index(3)]
        th_min = min(map(lambda x:len(x),th))
        it_left = list(filter(lambda x:x not in combine(th),it[0]))
        if (th_min>=it_left.count(1) or th_min>=it_left.count(2)):
            return 7  # 飞机
        else:
            return 6  # 三顺
    elif seq[1].count(2)>=1:
        return 5  # 双顺
    elif seq[1].count(1)>=1:
        return 4  # 单顺
    elif it[1].count(3)>=1 and it[1].count(1)>=1:
        return 3  # 三带一
    elif it[1].count(3)>=1:
        return 2  # 三张
    elif it[1].count(2)>=1:
        return 1  # 对牌
    elif it[1].count(1)>=1:
        return 0  # 单牌    

def calc_len(data,it):
    if data[1].count(2)>0 and len(it)>0 and min(it)<len(sequence)-3:#三带一
        c = min(it)
        it.remove(c)
        data[0][data[1].index(2)].append(c)
        data[1][data[1].index(2)] = 3
    if data[1].count(6)>0:#飞机
        l = len(data[0][data[1].index(6)])//3
        if len(data[0][data[1].index(1)])//2>=l:
            for i in range(l):
                c = data[0][data[1].index(1)][0]
                data[0][data[1].index(1)].remove(c)
                data[0][data[1].index(6)][0]+=c
                if len(data[0][data[1].index(1)])==0:
                    data[0].pop(data[0][data[1].index(1)].index(c))
            data[1][data[1].index(6)] = 7
        if len(it)>=l:
            for i in range(l):
                c = min(it)
                it.remove(c)
                data[0][data[1].index(6)].append(c)
            data[1][data[1].index(6)] = 7
    if it.count(len(sequence)-1)>0 and it.count(len(sequence)-2)>0:#王炸
        it.remove(len(sequence)-1)
        it.remove(len(sequence)-2)
        if data[1].count(8)>0:
            data[0][data[1].index(8)].append([len(sequence)-2,len(sequence)-1])
        else:
            data[0].append([len(sequence)-2,len(sequence)-1])
            data[1].append(8)
    return len(data[0])+len(it)

def split_seq(it,seq,temp,left,count,type):
    if seq[1].count(count)>0:
        temp[0] += copy.deepcopy((map(lambda x:x*count,seq[0][seq[1].index(count)])))
        temp[1].append(type)
        for j in seq[0][seq[1].index(count)]:
            for k in j:
                it[1][it[0].index(k)]-=count
                for _ in range(count): left.remove(k)
                if it[1][it[0].index(k)]==0:
                    it[1].pop(it[0].index(k))
                    it[0].remove(k)

def split_repeat(it,seq,temp,left,count,type):
    while it[1].count(count)>0:
        temp[0] += copy.deepcopy([[it[0][it[1].index(count)]]*count])
        temp[1].append(type)# 炸弹
        for _ in range(count): left.remove(it[0][it[1].index(count)])
        it[0].remove(it[0][it[1].index(count)])
        it[1].remove(count)
    
def best_way(me):
    ret,length = [],len(me)
    func = [[split_repeat,4,8],[split_seq,3,6],[split_seq,2,5],
        [split_seq,1,4],[split_repeat,3,2],[split_repeat,2,1]]
    for i in range(6):
        temp,left = [[],[]],me.copy()
        seq = has_sequence(me.copy())
        it = to_samecard_count(me.copy())        
        func.append(func[0])
        func.pop(0)
        
        for i in range(6):
            func[i][0](it,seq,temp,left,func[i][1],func[i][2])
            seq = has_sequence(left)

        temp_len = calc_len(temp,left)
        #print(temp,"left:",left,"len:",temp_len)
        if length>temp_len:
            length = temp_len
            ret = copy.deepcopy(temp)
            if len(left)>0:
                ret[0].append(left)
                ret[1].append(0)

    for x in ret[0]:
        print(to_card(x))
    return ret

def get_card(pre, me, out, desc):
    a = []
    me.sort(reverse=desc)
    d = best_way(me.copy())
    if len(pre)>0:
        pre.sort(reverse=desc)
        s=has_sequence(pre)
        if len(s[1])==0:
            for i in pre:
                for j in me:
                    if out!=None and out.count(j)>0:#否定了的牌
                        continue
                    if j > i and pre.count(i) <= me.count(j) and a.count(j)<pre.count(i):
                        a.append(j)
                        break
        else:
            seq = has_sequence(me.copy())
            if len(seq[1])!=0 and max(s[1]) in seq[1]:
                t = s[0][s[1].index(max(s[1]))]
                for k in seq[0][seq[1].index(max(s[1]))]:
                    if len(k)>=len(t[0]):
                        for j in range(len(k)-len(t[0])+1):
                            if k[j]>t[0][0]:
                                a = k[j:len(t[0])]
                                break
                        if len(a)>0:break

        if desc and len(a)==0 and d[1].count(8)>0:
            a+=d[0][d[1].index(8)]
    else:#主动出牌
        dd = list(filter(lambda x:x!=8,d[1])) if len(d[1])>1 else d[1]
        t = max(dd) if desc else min(dd)
        if t==0:
            a.append(d[0][d[1].index(t)][-1] if desc else d[0][d[1].index(t)][0])
        else:
            a += d[0][d[1].index(t)] if desc else d[0][d[1].index(t)]
        if len(d[0][d[1].index(t)])==1:
            d[0].pop(d[1].index(t))
            d[1].remove(t)
    return a

def select_card(pre, me, dizhu):
    if len(pre)>0:
        id = has_card(pre)
        print('上家:', pre, card[id] if id > -1 else 'unknow')
    if dizhu == 'me':
        ret = get_card(pre, me, None, False)
        return ret
    else:
        ret = get_card(pre, me, None, True)
        return ret

if __name__ == "__main__":
    while True:
        print('------------new turn-----------------')
        #item = [1, 2, 3,4,5,6, 7, 8, 9, 9, 10, 10, 10, 11, 11, 12, 13, 14]
        item = list(np.random.randint(len(sequence),size=17))
        item.sort()
        print('------------------',item)
        while True:
            start = time.time()
            pre = to_num(['3','4','5','6','7'])
            me = select_card(pre,item,'dizhu')
            if len(me)==0: print('put none card')
            else: print('put card:',me ,to_card(me))
            for i in me: item.remove(i)
            if len(item)==0: break
            print("time:",time.time()-start,'\r\n')
        